/**
 * @file cdnet_l0.c
 * @brief CDNET Layer 0协议实现 - 8位端口寻址
 * @details CDNET Layer 0协议的完整实现，支持8位端口寻址
 *          适用于简单网络场景，提供基本的点对点通信功能
 * 
 * @author 修改团队：mastwet@Undefine Innovation
 * @date 2024
 * @copyright MIT License
 * @note 基于Duke Fong的cdnet_l0实现
 */

#include "cdnet.h"

/**
 * @brief 写入Layer 0协议头部
 * @param pkt CDNET数据包指针
 * @param hdr 头部缓冲区指针
 * @return 头部大小（字节数），失败返回错误码
 * 
 * @note Layer 0头部格式：[源端口(1字节)][目标端口(1字节)]
 * @note 端口范围：0-127（最高位必须为0）
 * 
 * 地址验证规则：
 - 目标地址类型必须为0x00（单播）
 - 源端口和目标端口必须为8位（高9-15位必须为0）
 */
int cdn0_hdr_w(const cdn_pkt_t *pkt, uint8_t *hdr)
{
    const cdn_sockaddr_t *src = &pkt->src;
    const cdn_sockaddr_t *dst = &pkt->dst;

    /* 验证地址和端口格式 */
    if (dst->addr[0] != 0x00 || (src->port & 0xff80) != 0 || (dst->port & 0xff80) != 0)
        return CDN_ERR_ASSERT;

    /* 构建Layer 0头部 */
    hdr[0] = src->port;  /* 源端口 */
    hdr[1] = dst->port;  /* 目标端口 */
    return 2;            /* Layer 0头部大小 */
}

int cdn0_frame_w(cdn_pkt_t *pkt)
{
    uint8_t *frame = pkt->frm->dat;
    frame[0] = pkt->src.addr[2];
    frame[1] = pkt->dst.addr[2];
    int len = cdn0_hdr_w(pkt, frame + 3);
    if (len < 0)
        return len;
    frame[2] = pkt->len + len;
    return 0;
}

int cdn0_hdr_r(cdn_pkt_t *pkt, const uint8_t *hdr)
{
    cdn_sockaddr_t *src = &pkt->src;
    cdn_sockaddr_t *dst = &pkt->dst;

    if ((*hdr & 0x80) != 0)
        return CDN_ERR_ASSERT;
    
    src->addr[0] = 0;
    src->addr[1] = pkt->_l_net;
    dst->addr[0] = 0;
    dst->addr[1] = pkt->_l_net;
    src->addr[2] = pkt->_s_mac;
    dst->addr[2] = pkt->_d_mac;

    src->port = hdr[0];
    dst->port = hdr[1];
    
    if ((dst->port & 0x80) != 0)
        return CDN_ERR_ASSERT;
    
    return 2;
}

int cdn0_frame_r(cdn_pkt_t *pkt)
{
    uint8_t *frame = pkt->frm->dat;
    pkt->_s_mac = frame[0];
    pkt->_d_mac = frame[1];
    int len = cdn0_hdr_r(pkt, frame + 3);
    if (len < 0)
        return len;
    pkt->dat = frame + 3 + len;
    pkt->len = frame[2] - len;
    return 0;
}

int cdn_hdr_size_pkt(const cdn_pkt_t *pkt)
{
    if (pkt->src.addr[0] == 0x00 && pkt->dst.addr[0] == 0x00)
        return 2; // L0 header size
    else
        return cdn1_hdr_size(pkt); // L1 header size
}

int cdn_hdr_size_frm(const cd_frame_t *frm)
{
    if (frm->dat[3] & 0x80)
        return cdn1_hdr_size_frm(frm);
    else
        return 2; // L0 header size
}

static int cdn1_hdr_size(const cdn_pkt_t *pkt)
{
    int size = 1; // base header
    
    if (pkt->src.addr[0] == 0xa0) // multi-net
        size += 2;
    if (pkt->dst.addr[0] == 0xf0 || pkt->dst.addr[0] == 0xa0) // multi-cast or multi-net
        size += 2;
    
    size += 1; // src port
    if (pkt->src.port & 0xff00)
        size += 1;
    
    size += 1; // dst port
    if (pkt->dst.port & 0xff00)
        size += 1;
    
    return size;
}

static int cdn1_hdr_size_frm(const cd_frame_t *frm)
{
    uint8_t hdr = frm->dat[3];
    int size = 1; // base header
    
    if (hdr & 0x20) // multi-net src
        size += 2;
    if (hdr & 0x40) // multi-cast or multi-net dst
        size += 2;
    
    size += 1; // src port
    if (hdr & 0x02)
        size += 1;
    
    size += 1; // dst port
    if (hdr & 0x01)
        size += 1;
    
    return size;
}